from formatter import TypeTag


class Module:  # (String)
    def __init__(self, name, line_col):
        self.name = name  # type: str
        self.line_col = line_col

    def __str__(self):
        return "module " + str(self.name)


class Use:  # (String)
    def __init__(self, name, line_col):
        self.name = name  # type: str
        self.line_col = line_col

    def __str__(self):
        return "use " + str(self.name)


class Def:  # (TypeSymbol, [DefMember])
    def __init__(self, type_symbol, parent_type_ref_symbol, def_members, line_col):
        self.type_symbol = type_symbol  # type: TypeSymbol
        self.def_members = def_members  # type: list[DefMember]
        self.parent_type_ref_symbol = parent_type_ref_symbol  # type: TypeRefSymbol
        self.parent_type_tag = None  # type: TypeTag
        self.line_col = line_col

    def __str__(self):
        if self.parent_type_ref_symbol:
            return "def " + str(self.type_symbol) + ' : ' + str(self.parent_type_ref_symbol) + " {" + ",".join(
                str(e) for e in self.def_members) + "}"
        return "def " + str(self.type_symbol) + " {" + ",".join(str(e) for e in self.def_members) + "}"


class TypeSymbol:  # (str, [GenericTypeSymbol])
    def __init__(self, type_name, generic_type_symbols, line_col):
        self.type_name = type_name  # type: str
        self.generic_type_symbols = generic_type_symbols  # type: list[GenericTypeSymbol]
        self.line_col = line_col
        # will be filled after format
        self.a_def = None  # type: Def
        # will be filled after format
        self.type_tag = None  # type: TypeTag
        self.built_in = False  # type: bool

    def __str__(self):
        ast_name = 'TypeSymbol '
        if self.generic_type_symbols is not None:
            return ast_name + str(self.type_name) + "<" + ",".join(str(e) for e in self.generic_type_symbols) + ">"
        else:
            return ast_name + str(self.type_name)


class TypeRefSymbol:  # (str, [GenericArgumentSymbol])
    def __init__(self, type_name, type_ref_symbols, line_col):
        self.type_name = type_name  # type: str
        self.type_ref_symbols = type_ref_symbols  # type: list[TypeRefSymbol]
        self.line_col = line_col
        # will be filled after format
        self.type_tag = None  # type: TypeTag

    def __str__(self):
        type_name = 'TypeRefSymbol '
        if self.type_ref_symbols is not None:
            return type_name + str(self.type_name) + "<" + ",".join(str(e) for e in self.type_ref_symbols) + ">"
        else:
            return type_name + str(self.type_name)


class GenericTypeSymbol:  # (str, TypeSymbol)
    def __init__(self, type_name, type_ref_symbol, line_col):
        self.type_name = type_name  # type: str
        self.type_ref_symbol = type_ref_symbol  # type: list[TypeRefSymbol]
        self.line_col = line_col

        # this tag is set here instead of in `self.type_ref_symbol`
        # because after parsing, the `type_ref_symbol` might be None
        # on where will add a default tag in `formatter`

        # the real type tag (top type); will be filled after format
        self.type_symbol_tag = None  # type: TypeTag
        # the generic type parameter tag; will be filled after format
        self.type_tag = None  # type: TypeTag

    def __str__(self):
        type_name = 'GenericTypeSymbol '
        if not self.type_name:
            return type_name + str(self.type_ref_symbol)
        if not self.type_ref_symbol:
            return type_name + str(self.type_name)
        return type_name + str(self.type_name) + ":" + str(self.type_ref_symbol)


class OptionalTypeSymbol:  # (TypeSymbol, optional)
    def __init__(self, type_ref_symbol, optional, line_col):
        self.type_ref_symbol = type_ref_symbol  # type: TypeRefSymbol
        self.optional = optional  # type: bool
        self.line_col = line_col

    def __str__(self):
        s = str(self.type_ref_symbol)
        if self.optional:
            s += "?"
        return s


class DefMember:  # (String, OptionalTypeSymbol)
    def __init__(self, key, optional_type_symbol, line_col):
        self.key = key  # type: String
        self.optional_type_symbol = optional_type_symbol  # type: OptionalTypeSymbol
        self.line_col = line_col

    def __str__(self):
        return str(self.key) + ":" + str(self.optional_type_symbol)


class TypeValue:
    def __init__(self, type_ref_symbol, value, line_col):
        self.type_ref_symbol = type_ref_symbol  # type: TypeRefSymbol
        self.value = value  # type: String|Number|Object|Null|Boolean|Array|Set
        self.line_col = line_col

    def __str__(self):
        if self.type_ref_symbol:
            return str(self.type_ref_symbol) + " " + str(self.value)
        return str(self.value)


class Value:
    def __init__(self, line_col):
        self.line_col = line_col
        self.res_value = None


class Object(Value):  # ([Member])
    def __init__(self, members, line_col):
        Value.__init__(self, line_col)
        self.members = members  # type: list[Member]

    def __str__(self):
        return "{" + ",".join(str(e) for e in self.members) + "}"


class Member:  # (String, TypeValue)
    def __init__(self, key, value, line_col):
        self.key = key  # type: String
        self.value = value  # type: TypeValue
        self.line_col = line_col

    def __str__(self):
        return str(self.key) + ":" + str(self.value)


class Null(Value):  # ()
    def __init__(self, line_col):
        Value.__init__(self, line_col)

    def __str__(self):
        return "null"


class String(Value):
    def __init__(self, string, line_col):
        Value.__init__(self, line_col)
        self.string = string  # type: str

    def __str__(self):
        return "\"" + self.string + "\""


class Boolean(Value):
    def __init__(self, boolean, line_col):
        Value.__init__(self, line_col)
        self.boolean = boolean  # type: bool

    def __str__(self):
        return str(self.boolean)


class Number(Value):  # (int|float, bool)
    def __init__(self, num, is_float, line_col):
        Value.__init__(self, line_col)
        self.num = num  # type: int|float
        self.is_float = is_float  # type: bool

    def __str__(self):
        return str(self.num)


class Array(Value):
    def __init__(self, elements, line_col):
        Value.__init__(self, line_col)
        self.elements = elements  # type: list[TypeValue]

    def __str__(self):
        return '[' + ",".join(str(e) for e in self.elements) + ']'


class Set(Value):
    def __init__(self, elements, line_col):
        Value.__init__(self, line_col)
        self.elements = elements  # type: list[TypeValue]

    def __str__(self):
        return '(' + ",".join(str(e) for e in self.elements) + ')'
